package org.example.service.Impl;

import cn.hutool.core.date.DateTime;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;
import org.example.commonapi.domain.*;
import org.example.commonapi.domain.onlineCode.*;
import org.example.commonapi.dto.LoginInfo;
import org.example.commonapi.mapper.UserMapper;
import org.example.commonapi.mongo.CodeRepository;
import org.example.commonapi.mongo.CodeTopicRepository;
import org.example.commonapi.mongo.CommitOjRepository;
import org.example.commonapi.mongo.CommitOwnRepository;
import org.example.commonapi.mongo.CommitRecordRepository;
import org.example.commonapi.mongo.CommitStatisticsRepository;
import org.example.commonapi.mongo.RankForOjRepository;
import org.example.commonapi.mongo.TestGroupRepository;
import org.example.commonapi.util.JwtUtil;
import org.example.service.CommitService;
import org.example.utils.OkHttpUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

@Service
public class CommitServiceImpl implements CommitService {

    @Autowired
    private OkHttpUtil client;

    @Resource
    private JwtUtil jwtUtil;

    @Autowired
    private CommitOjRepository commitOjRepository;

    @Autowired
    private RankForOjRepository rankForOjRepository;

    @Autowired
    private TestGroupRepository testGroupRepository;

    @Autowired
    private CodeTopicRepository codeTopicRepository;

    @Autowired
    private CommitRecordRepository commitRecordRepository;

    @Autowired
    private CodeRepository codeRepository;

    @Autowired
    private CommitStatisticsRepository commitStatisticsRepository;

    @Autowired
    private CommitOwnRepository commitOwnRepository;

    @Autowired
    private UserMapper userMapper;

    @Value("${onlinecode.url}")
    private String baseUrl;

    @Override
    public R<Object> commitOi(OiCode oiCode) {
        // 创建OI代码对象
        CodeRoot oi = new CodeRoot(oiCode, "oi");
        // 将OI代码对象转换为JSON格式并发送到指定的URL进行判断
        String post = client.post(baseUrl + "/judge", JSON.toJSONString(oi));
        // 解析返回的JSON数据
        SimpleJson json = JSON.parseObject(post, SimpleJson.class);
        // 根据返回的代码进行不同的处理
        if (json.getCode() != 200) {
            return new R<>(json,json.getCode(),json.getMsg());
        } else {
            return new R<>( json,200, json.getMsg());
        }
    }

    @Override
    public R<Object> commitOj(OjCode ojCode, HttpServletRequest request) {
        try {
            // 从数据库中获取问题的相关信息
            Code byProblemId = getProblemById(ojCode.getProblemId());
            // 创建OJ代码对象
            CodeRoot oj = createCodeRoot(ojCode, byProblemId);
            // 将OJ代码对象转换为JSON格式并发送到指定的URL进行判断
            SimpleJson json = postCodeForJudgement(oj);
            // 创建代码结果对象
            CodeResultForOJ codeResult = createCodeResult(ojCode, json,request);
            // 处理判断结果
            return handleJudgementResult(json, codeResult, ojCode,request);
        } catch (Exception e) {
            return new R<>(null,500, e.getMessage());
        }
    }

    private Code getProblemById(String problemId) throws Exception {
        // 从数据库中获取问题的相关信息
        Code byProblemId = codeRepository.findByProblemId(problemId);
        if (byProblemId == null) {
            throw new Exception("题目不存在");
        }
        return byProblemId;
    }

    private CodeRoot createCodeRoot(OjCode ojCode, Code byProblemId) {
        // 创建标准代码对象
        StandardCode standardCode = new StandardCode();
        standardCode.setRunCodeId(byProblemId.getCodeId());
        standardCode.setInputFileContext(byProblemId.getTestGroupId());
        standardCode.setRunCode("");
        standardCode.setRunCodeLanguage("");
        standardCode.setInputFileType("file");
        // 创建OJ代码对象
        return new CodeRoot(ojCode, "oj", standardCode);
    }

    private SimpleJson postCodeForJudgement(CodeRoot oj) throws JSONException {
        // 将OJ代码对象转换为JSON格式并发送到指定的URL进行判断
        String post = client.post(baseUrl + "/judge", JSON.toJSONString(oj));
        // 解析返回的JSON数据
        return JSON.parseObject(post, SimpleJson.class);
    }

    private CodeResultForOJ createCodeResult(OjCode ojCode, SimpleJson json,HttpServletRequest request) {
        // 创建代码结果对象
        CodeResultForOJ codeResult = new CodeResultForOJ();
        codeResult.setCode(ojCode.getCode());
        codeResult.setLanguage(ojCode.getLanguage());
        Long userId = jwtUtil.parseJwt(request.getHeader("token")).getId();
        User user = userMapper.selectById(userId);
        System.out.println("用户信息：" + user);
        codeResult.setUsername(user.getNo());
        codeResult.setNikeName(user.getUsername());
        codeResult.setTime(DateTime.now().toString());
        codeResult.setStatue(json.getMsg());
        codeResult.setRunTime("0");
        codeResult.setMemory("0");
        codeResult.setRunTime("0");
        return codeResult;
    }

    /**
     * 处理判断结果。
     *
     * @param json       判断结果
     * @param codeResult 代码结果
     * @param ojCode     代码信息
     * @return 返回处理结果
     * @throws JSONException JSON异常
     */
    private R<Object> handleJudgementResult(SimpleJson json, CodeResultForOJ codeResult, OjCode ojCode,HttpServletRequest request) throws JSONException {
        // 获取输出案例组根对象列表
        List<OutCaseGroupRoot> outCaseGroupRoots = getOutCaseGroupRoots(json);
        CodeResultForOJ save = new CodeResultForOJ();
        String problemId = ojCode.getProblemId();
        // 从数据库中查找题目
        Optional<CodeTopic> byId = codeTopicRepository.findById(problemId);
        // 如果题目不存在，返回错误
        if (byId.isEmpty()) {
            return new R<>(null,400, "题目不存在");
        }
        CodeTopic codeTopic = byId.get();
        // 设置提交时间
        codeResult.setSubmitTime(DateTime.now().toString());
        save.setSubmitTime(DateTime.now().toString());
        // 设置题目ID
        codeResult.setProblemId(problemId);
        save.setProblemId(problemId);
        // 根据返回的代码进行不同的处理
        switch (json.getCode()) {
            case 200:
                // 处理成功的判断结果
                handleSuccessfulJudgement(json, codeResult, outCaseGroupRoots);
                // 保存判断结果
                save = commitOjRepository.save(codeResult);
                // 更新OJ排名
                updateRankForOj(codeResult, save,request);
                // 更新题目的提交数和通过数
                codeTopic.setPassNum(codeTopic.getPassNum() + 1);
                codeTopic.setCommitNum(codeTopic.getCommitNum() + 1);
                // 保存题目信息
                codeTopicRepository.save(codeTopic);
                // 保存提交记录
                CommitRecord commitRecord = new CommitRecord(ojCode, codeResult);
                commitRecordRepository.save(commitRecord);
                // 保存提交统计信息
                saveCommitStatistics(codeResult, ojCode, commitRecord);
                // 检查是否已经有提交记录
                List<CommitOwn> byProblemId = commitOwnRepository.findByProblemIdAndUsername(ojCode.getProblemId(), codeResult.getUsername());
                // 如果没有提交记录，创建新的提交记录
                if (byProblemId == null || byProblemId.isEmpty()) {
                    CommitOwn commitOwn = new CommitOwn(ojCode.getProblemId(), codeResult.getUsername(), true);
                    commitOwnRepository.save(commitOwn);
                }
                // 返回成功结果
                return new R<>(save,200, json.getMsg());
            case 503:
                // 设置结果为错误
                codeResult.setResult(2);
                // 设置输入信息
                codeResult.setInput(json.getData().toString());
                // 保存判断结果
                save = commitOjRepository.save(codeResult);
                // 更新题目的提交数
                codeTopic.setCommitNum(codeTopic.getCommitNum() + 1);
                // 保存题目信息
                codeTopicRepository.save(codeTopic);
                // 返回错误结果
                return new R<>(save,503, json.getMsg());
            case 400:
                // 处理失败的判断结果
                handleFailedJudgement(codeResult, outCaseGroupRoots);
                // 保存判断结果
                save = commitOjRepository.save(codeResult);
                // 更新题目的提交数
                codeTopic.setCommitNum(codeTopic.getCommitNum() + 1);
                // 保存题目信息
                codeTopicRepository.save(codeTopic);
                // 返回错误结果
                return new R<>(save,400, json.getMsg());
            default:
                // 设置结果为错误
                codeResult.setResult(2);
                // 保存判断结果
                save = commitOjRepository.save(codeResult);
                // 更新题目的提交数
                codeTopic.setCommitNum(codeTopic.getCommitNum() + 1);
                // 保存题目信息
                codeTopicRepository.save(codeTopic);
                // 返回错误结果
                return new R<>(save,500, json.getMsg());
        }
    }

    private List<OutCaseGroupRoot> getOutCaseGroupRoots(SimpleJson json) throws JSONException {
        // 获取输出案例组根对象列表
        if (json.getData() == null) {
            throw new JSONException(json.getMsg());
        }
        try {
            return JSON.parseArray(json.getData().toString(), OutCaseGroupRoot.class);
        } catch (JSONException e) {
            return new ArrayList<>();
        }
    }

    private void handleSuccessfulJudgement(SimpleJson json, CodeResultForOJ codeResult, List<OutCaseGroupRoot> outCaseGroupRoots) {
        // 处理成功的判断结果
        long memory = 0L;
        long time = 0L;
        long runTime = 0L;
        for (OutCaseGroupRoot outCaseGroupRoot : outCaseGroupRoots) {
            memory += outCaseGroupRoot.getGroupResult().getMemory();
            time += outCaseGroupRoot.getGroupResult().getTime();
            runTime += outCaseGroupRoot.getGroupResult().getRunTime();
        }
        memory = memory / outCaseGroupRoots.size();
        time = time / outCaseGroupRoots.size();
        runTime = runTime / outCaseGroupRoots.size();
        codeResult.setTime(time + "ms");
        codeResult.setRunTime(runTime + "ms");
        codeResult.setMemory(memory + "kb");
        List<Boolean> outCaseResults = new ArrayList<>();
        for (OutCaseGroupRoot outCaseGroupRoot : outCaseGroupRoots) {
            boolean groupAccepted = outCaseGroupRoot.isGroupAccepted();
            outCaseResults.add(groupAccepted);
        }
        codeResult.setOutCaseResults(outCaseResults);
        codeResult.setOutCaseGroupRoots(outCaseGroupRoots);
        codeResult.setResult(0);
    }

    private void updateRankForOj(CodeResultForOJ codeResult, CodeResultForOJ save,HttpServletRequest request) {
        Long userId = jwtUtil.parseJwt(request.getHeader("token")).getId();
        User user = userMapper.selectById(userId);
        // 更新OJ排名
        RankForOj rankForOj = new RankForOj();
        rankForOj.setUsername(codeResult.getUsername());
        rankForOj.setNikeName(codeResult.getNikeName());
        rankForOj.setQuestionId(save.getId());
        rankForOj.setSubmitNum(save.getId());
        rankForOj.setTime(Integer.parseInt(codeResult.getTime().replace("ms", "")));
        rankForOj.setMemory(Integer.parseInt(codeResult.getMemory().replace("kb", "")));
        rankForOj.setRunTime(Integer.parseInt(codeResult.getRunTime().replace("ms", "")));
        rankForOj.setHeadImg(user.getHeaderLink());
        RankForOj byUsername = rankForOjRepository.findByUsername(codeResult.getUsername());
        if (byUsername == null) {
            rankForOjRepository.save(rankForOj);
        } else {
            if (byUsername.getTime() > rankForOj.getTime()) {
                byUsername.setTime(rankForOj.getTime());
            }
            if (byUsername.getMemory() > rankForOj.getMemory()) {
                byUsername.setMemory(rankForOj.getMemory());
            }
            if (byUsername.getRunTime() > rankForOj.getRunTime()) {
                byUsername.setRunTime(rankForOj.getRunTime());
            }
            rankForOjRepository.save(byUsername);
        }
    }

    /**
     * 保存提交统计信息。
     *
     * @param codeResult   包含代码执行结果的信息，如用时、内存消耗和运行时间等。
     * @param ojCode       表示一次在线判题的代码信息。
     * @param commitRecord 提交记录，包含问题ID等信息。
     */
    private void saveCommitStatistics(CodeResultForOJ codeResult, OjCode ojCode, CommitRecord commitRecord) {
        // 根据问题ID查询已有的提交统计信息
        List<CommitStatistics> byProblemId = commitStatisticsRepository.findByProblemId(commitRecord.getProblemId());
        // 处理并转换运行时间、内存和用时单位
        String time = safeReplace(codeResult.getTime(), "ms", "");
        String memory = safeReplace(codeResult.getMemory(), "kb", "");
        String runTime = safeReplace(codeResult.getRunTime(), "ms", "");
        if (byProblemId.isEmpty()) {
            // 如果没有找到提交统计信息，则为三种类型的提交各创建一条记录
            CommitStatistics commitStatistics1 = new CommitStatistics(ojCode.getProblemId(), codeResult.getUsername(), 0, time, 1);
            commitStatisticsRepository.save(commitStatistics1);
            CommitStatistics commitStatistics2 = new CommitStatistics(ojCode.getProblemId(), codeResult.getUsername(), 1, memory, 1);
            commitStatisticsRepository.save(commitStatistics2);
            CommitStatistics commitStatistics3 = new CommitStatistics(ojCode.getProblemId(), codeResult.getUsername(), 2, runTime, 1);
            commitStatisticsRepository.save(commitStatistics3);
        } else {
            boolean f1 = false;
            boolean f2 = false;
            boolean f3 = false;
            for (CommitStatistics commitStatistics : byProblemId) {
                if (commitStatistics.getType() == 0) {
                    if (commitStatistics.getKey().equals(time)) {
                        commitStatistics.setValue(commitStatistics.getValue() + 1);
                        commitStatisticsRepository.save(commitStatistics);
                        f1 = true;
                    }
                }
                if (commitStatistics.getType() == 1) {
                    if (commitStatistics.getKey().equals(memory)) {
                        commitStatistics.setValue(commitStatistics.getValue() + 1);
                        commitStatisticsRepository.save(commitStatistics);
                        f2 = true;
                    }
                }
                if (commitStatistics.getType() == 2) {
                    if (commitStatistics.getKey().equals(runTime)) {
                        commitStatistics.setValue(commitStatistics.getValue() + 1);
                        commitStatisticsRepository.save(commitStatistics);
                        f3 = true;
                    }
                }
            }
            if (!f1) {
                CommitStatistics commitStatistics1 = new CommitStatistics(ojCode.getProblemId(), codeResult.getUsername(), 0, time, 1);
                commitStatisticsRepository.save(commitStatistics1);
            }
            if (!f2) {
                CommitStatistics commitStatistics2 = new CommitStatistics(ojCode.getProblemId(), codeResult.getUsername(), 1, memory, 1);
                commitStatisticsRepository.save(commitStatistics2);
            }
            if (!f3) {
                CommitStatistics commitStatistics3 = new CommitStatistics(ojCode.getProblemId(), codeResult.getUsername(), 2, runTime, 1);
                commitStatisticsRepository.save(commitStatistics3);
            }
        }
    }

    private void handleFailedJudgement(CodeResultForOJ codeResult, List<OutCaseGroupRoot> outCaseGroupRoots) {
        // 处理失败的判断结果
        codeResult.setOutCaseGroupRoots(outCaseGroupRoots);
        codeResult.setResult(1);
        setValueForError(outCaseGroupRoots, codeResult);
    }

    private static void setValueForError(List<OutCaseGroupRoot> outCaseGroupRoots, CodeResultForOJ codeResult) {
        // 设置错误信息
        for (OutCaseGroupRoot outCaseGroupRoot : outCaseGroupRoots) {
            if (!outCaseGroupRoot.isGroupAccepted()) {
                for (OutTestCaseGroup outTestCaseGroup : outCaseGroupRoot.getOutput()) {
                    if (!outTestCaseGroup.isAccepted()) {
                        codeResult.setInput(outTestCaseGroup.getInput());
                        codeResult.setSaOutput(outTestCaseGroup.getSaOutput());
                        codeResult.setTaOutput(outTestCaseGroup.getTaOutput());
                        return;
                    }
                }
            }
        }
    }

    private String safeReplace(String input, String target, String replacement) {
        if (input != null && !input.isEmpty() && input.contains(target)) {
            return input.replace(target, replacement);
        }
        return input;
    }
}
